/**
 * 
 */
package core;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.StringTokenizer;

import spatialindex.rtree.RTree;
import spatialindex.spatialindex.ISpatialIndex;
import spatialindex.storagemanager.DiskStorageManager;
import spatialindex.storagemanager.IBuffer;
import spatialindex.storagemanager.IStorageManager;
import spatialindex.storagemanager.PropertySet;
import spatialindex.storagemanager.RandomEvictionsBuffer;
import util.Constants;
import util.NBGStrParser;

/**
 * NBG=network based generator
 * 
 * @author Xiaohui
 * 
 * @version 0.1 db conn removed
 * @version 0.2 multiple queries supported
 * @version 0.3 remember moids that are going to pass a junction
 */
public class TrajIndex {
	Connection con;
	

	
	// enable look up from id to check points
	

	RichEdge[] richEdgeArray;
	// eid to richEdgeArray index mapping
	HashMap<Long, Integer> eid2Index;

	public TrajIndex() {
		// getConn();;
		// this.richEdgeArray = new RichEdge[Constants.numEdges];
		// this.eid2Index = new HashMap<Long, Integer>();
		// createRichEdgeArray(Constants.edgeIdFile);
	}

	private void createRichEdgeArray(String edgeidfile) {
		BufferedReader in;
		try {
			in = new BufferedReader(new FileReader(edgeidfile));
			String str;
			int index = 0;
			long currEid;
			while ((str = in.readLine()) != null) {
				currEid = Long.parseLong(str);
				this.richEdgeArray[index] = new RichEdge(currEid,
						Constants.numTimePoints);
				this.eid2Index.put(currEid, index);
				index++;

			}
			in.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	private double calPassTime(int currtime, double currPosx, double currPosy,
			double junctionx, double junctiony, double speed) {
		double dist = Math.sqrt(Math.pow(currPosx - junctionx, 2.0)
				+ Math.pow(currPosy - junctiony, 2.0));
		return currtime - dist / speed;
	}


	private void getConn() {
		if (con == null) {
			try {
				// Register the JDBC driver for MySQL.
				Class.forName("com.mysql.jdbc.Driver");
				con = DriverManager.getConnection(Constants.trajDBURL,
						Constants.username, Constants.password);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}

	
	private int getPointIDFromDB(Statement st, double xval, double yval) {
		String sql = "select id from " + Constants.loc2idTableName
				+ " where xloc=" + xval + " AND " + "yloc=" + yval;
		try {

			ResultSet rs = st.executeQuery(sql);
			while (rs.next()) {
				return rs.getInt("id");
			}
			rs.close();
		} catch (SQLException e) {
			e.printStackTrace();
		}
		return -1;
	}

	
	// INSERT INTO currloc2id (xloc, yloc) values (1.0, 2.0), (3.0, 4.0);
	// normalized data expected
	public void insertIntoDB(String normalizedFilename) {
		String removedata = "delete from " + Constants.loc2idTableName;
		String removeRawData = "delete from " + Constants.rawDataTableName;
		String loaddata = "load data infile '" + normalizedFilename
				+ "' INTO TABLE " + Constants.rawDataTableName;
		String movedata = "insert into " + Constants.loc2idTableName
				+ "(xloc, yloc)"
				+ " select distinct x_nextNode, y_nextNode from "
				+ Constants.rawDataTableName;

		// System.out.println(loaddata);
		// System.out.println(movedata);
		try {
			Statement st = con.createStatement();
			st.execute(removedata);
			st.execute(removeRawData);

			st.execute(loaddata);
			st.execute(movedata);
			// System.out.println("End Insertion");
			st.close();
		} catch (Exception e) {
			e.printStackTrace();
		}

	}

	

	/**
	 * 1) Read raw data<br>
	 * Heap space problem
	 * 
	 * @param nbgDataFile
	 */
	public void loadRawDataEdgeImpl(String nbgDataFile) {
		System.out.println("Loading raw data ...");
		BufferedReader in;
		try {
			in = new BufferedReader(new FileReader(nbgDataFile));
			String str;
			int count = 1;
			NBGStrParser parser = new NBGStrParser();
			while ((str = in.readLine()) != null) {
				if (str.startsWith("#")) {
					continue;
				}
				if (count % 10000 == 0) {
					System.out.println("Loaind raw data progress: " + count);
				}
				count++;
				parser.setNBGStr(str);
				int timepoint = parser.getTimepoint();
				int index = eid2Index.get(parser.getRoadid());
				// time stamp points to a list of oid
				this.richEdgeArray[index].add(timepoint, parser.getOid());

			}
			in.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * spatial + temporal filtering Heap space problem
	 * 
	 * @param qparser
	 * @throws Exception
	 */
	public Set<Integer> QueryProcessingEdgeImpl(QueryParser qparser,
			String queryType, String netRTreeFile, RoadNetworkIndex netIndexer)
			throws Exception {
		Set<Integer> candidateOid = new HashSet<Integer>();
		// intersect with network RTree
		Set<Long> rids = netIndexer.RTreeQuery4Rid(qparser, netRTreeFile,
				queryType);
		RichEdge currEdge;
		int qStartTime = (int) qparser.getStartTime();
		int qEndTime = (int) qparser.getEndTime() + 1;
		for (long rid : rids) {
			currEdge = getRichEdge(rid);
			for (int i = qStartTime; i < qEndTime; i++) {
				LinkedList listMOId = currEdge.get(i);
				for (Object obj : listMOId) {
					candidateOid.add((Integer) obj);
				}
			}
		}
		return candidateOid;
	}

	private RichEdge getRichEdge(long rid) {
		return richEdgeArray[eid2Index.get(rid)];
	}

	

	// void RTreeLoad(String treeFile, int capacity, List<CheckPoint> list,
	// String ss) {
	// // ss="p", point
	// // ss="r", region
	// if (ss.equals("r")) {
	// System.err.println("NOT IMPLEMENTED YET");
	// System.exit(-1);
	// } else if (ss.equals("p")) {
	// try {
	// // Create a disk based storage manager.
	// PropertySet ps = new PropertySet();
	//
	// Boolean b = new Boolean(true);
	// ps.setProperty("Overwrite", b);
	// // overwrite the file if it exists.
	//
	// ps.setProperty("FileName", treeFile);
	// // .idx and .dat extensions will be added.
	//
	// Integer i = new Integer(4096);
	// ps.setProperty("PageSize", i);
	// // specify the page size. Since the index may also contain user
	// // defined data
	// // there is no way to know how big a single node may become. The
	// // storage manager
	// // will use multiple pages per node if needed. Off course this
	// // will
	// // slow down performance.
	//
	// IStorageManager diskfile = new DiskStorageManager(ps);
	//
	// IBuffer file = new RandomEvictionsBuffer(diskfile, 10, false);
	// // applies a main memory random buffer on top of the persistent
	// // storage manager
	// // (LRU buffer, etc can be created the same way).
	//
	// // Create a new, empty, RTree with dimensionality 2, minimum
	// // load
	// // 70%, using "file" as
	// // the StorageManager and the RSTAR splitting policy.
	// PropertySet ps2 = new PropertySet();
	//
	// Double f = new Double(0.7);
	// ps2.setProperty("FillFactor", f);
	//
	// i = new Integer(capacity);
	// ps2.setProperty("IndexCapacity", i);
	// ps2.setProperty("LeafCapacity", i);
	// // Index capacity and leaf capacity may be different.
	//
	// i = new Integer(2);
	// ps2.setProperty("Dimension", i);
	//
	// ISpatialIndex tree = new RTree(ps2, file);
	//
	// int count = 0;
	//
	// long start = System.currentTimeMillis();
	//
	// for (CheckPoint cp : list) {
	// // insert
	// String data = cp.toString();
	// tree.insertData(data.getBytes(), cp, cp.getId()); // main
	// // memory
	// // example of passing a null pointer as the associated
	// // data.
	// if ((count % 1000) == 0)
	// System.err.println(count);
	// count++;
	// }
	//
	// long end = System.currentTimeMillis();
	//
	// System.err.println("Operations: " + count);
	// System.err.println(tree);
	// System.err.println("Seconds: " + ((end - start) / 1000.0f));
	//
	// Integer indexID = (Integer) ps2.getProperty("IndexIdentifier");
	// System.err.println("Index ID: " + indexID);
	//
	// boolean ret = tree.isIndexValid();
	// if (ret == false)
	// System.err.println("Structure is INVALID!");
	//
	// // flush all pending changes to persistent storage (needed since
	// // Java might not call finalize when JVM exits).
	// tree.flush();
	// } catch (Exception e) {
	// e.printStackTrace();
	// }
	//
	// } else {
	// System.err
	// .println("Usage: RTreeLoad input_file tree_file datalist  capacity datatype[r|p]");
	// System.exit(-1);
	// }
	// }

	/**
	 * Called once for every new set of raw data loading <br>
	 * ss="p", point; ss="r",region
	 * 
	 * 
	 * @param treeFile
	 * @param capacity
	 * @param allPoints
	 * @param ss
	 */
	private void RTreeLoadCheckPoint(String treeFile, int capacity,
			HashMap<Integer, CheckPoint> allPoints, String ss) {
		System.out.println("Loading check points into RTree...");
		if (ss.equals("r")) {
			System.err.println("NOT IMPLEMENTED YET");
			System.exit(-1);
		} else if (ss.equals("p")) {
			try {
				// Create a disk based storage manager.
				PropertySet ps = new PropertySet();
				Boolean b = new Boolean(true);
				// overwrite the file if it exists.
				ps.setProperty("Overwrite", b);
				// fileName, .idx and .dat extensions will be added.
				ps.setProperty("FileName", treeFile);
				Integer i = new Integer(4096);
				ps.setProperty("PageSize", i);
				IStorageManager diskfile = new DiskStorageManager(ps);

				IBuffer file = new RandomEvictionsBuffer(diskfile, 10, false);
				PropertySet ps2 = new PropertySet();

				Double f = new Double(0.7);
				ps2.setProperty("FillFactor", f);

				i = new Integer(capacity);
				ps2.setProperty("IndexCapacity", i);
				ps2.setProperty("LeafCapacity", i);
				// Index capacity and leaf capacity may be different.

				i = new Integer(2);
				ps2.setProperty("Dimension", i);

				ISpatialIndex tree = new RTree(ps2, file);

				for (Integer integer : allPoints.keySet()) {
					// insert
					CheckPoint cp = allPoints.get(integer);
					// main memory example of passing a null pointer as the
					// associated data.
					if (cp instanceof StartPoint
							|| cp instanceof DisappearPoint) {
						tree
								.insertData(cp.getData().getBytes(), cp, cp
										.getId());
					}

				}

				Integer indexID = (Integer) ps2.getProperty("IndexIdentifier");
				System.err.println("Index ID: " + indexID);

				boolean ret = tree.isIndexValid();
				if (ret == false)
					System.err.println("Structure is INVALID!");

				// flush all pending changes to persistent storage (needed since
				// Java might not call finalize when JVM exits).
				tree.flush();
			} catch (Exception e) {
				e.printStackTrace();
			}

		} else {
			System.err
					.println("Usage: RTreeLoad input_file tree_file datalist  capacity datatype[r|p]");
			System.exit(-1);
		}
	}

}
